#ifndef __BLOCK__QUEUE__HPP__
#define __BLOCK__QUEUE__HPP__

#include <pthread.h>
#include <iostream>
#include <queue>

template <typename T>
class BlockQueue
{
private:
    bool IsFull()
    {
        return _block_queue.size() == _cap;
    }
    
    bool IsEmpty()
    {
        return _block_queue.size() == 0;
    }
public:
    BlockQueue(int cap) : _cap(cap),_productor_wait_num(0),_consumer_wait_num(0)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_productor_cond, nullptr);
        pthread_cond_init(&_consumer_cond, nullptr);
    }

    void Enqueue(const T &in) // 生产者接口
    {
        pthread_mutex_lock(&_mutex);
        // if (IsFull()) // Bug
        while (IsFull()) // 保证数据健壮性
        {
            //1. pthread_cond_wait调用时，a：让线程进入等待，b：释放曾经持有的_mutex锁
            //只要有等待，就一点定会有唤醒，这个时候就要从这里继续往下走
            //这个时候pthread_cond_wait要求这个线程继续竞争锁
            //之前是安全的。
            _productor_wait_num++;
            pthread_cond_wait(&_productor_cond, &_mutex); //如果由很多线程被唤醒（假设5个），broadcast,这个时候5个一起竞争一把锁
            _productor_wait_num--;
            //但是如果这个时候只有一个数据呢？这个数据会被其中一个拿走，但是其他4给以及到这个位置了，这个时候剩下的4个线程又去竞争这把锁
            //但是这个时候没有数据了，就算拿到锁了，往下执行但是没数据了还去操作就会报错，所以这4个线程被伪唤醒，所以这里不能用if而是用while
            //之后：安全的
        }
        //进行生产
        _block_queue.push(in);
        //通知消费者消费
        if (_consumer_wait_num > 0) pthread_cond_signal(&_consumer_cond);

        pthread_mutex_unlock(&_mutex);
    }

    void Pop(T *out) // 消费者接口，输出型参数
    {
        pthread_mutex_lock(&_mutex);
        // if (IsEmpty())//Bug
        while (IsEmpty())//Bug
        {
            _consumer_wait_num++;
            pthread_cond_wait(&_consumer_cond, &_mutex);
            _consumer_wait_num--;
        }
        //读数据
        *out = _block_queue.front();
        _block_queue.pop();
        //通知生产者生产
        if (_productor_wait_num > 0) pthread_cond_signal(&_productor_cond);

        pthread_mutex_unlock(&_mutex);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_productor_cond);
        pthread_cond_destroy(&_consumer_cond);
    }

private:
    std::queue<T> _block_queue;     // 阻塞队列，是被整体使用的
    int _cap;                       // 总上限
    pthread_mutex_t _mutex;         // 保护_block_queue队列
    pthread_cond_t _productor_cond; // 专门个生产者的条件变量
    pthread_cond_t _consumer_cond;  // 专门个消费者的条件变量

    int _productor_wait_num; //生产者等待的数量
    int _consumer_wait_num;  //消费者等待的数量
};

#endif
